#include "Druide.h"

//#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const unsigned int Druide::nbr_comp = 75;

const unsigned int Druide::nbr_restric = 0;

/**
 * Les homs des sphères sont accentuées "à la LaTeX", afin de
 * simplifier la gestion des noms de dossiers.
 */
const vector<ListComp> Druide::Comp = list_of
  (ListComp("Amitié avec les animaux"                    , 5 ))
  (ListComp("Communication avec les créatures sylvestres", 10))
  (ListComp("Déplacement silencieux"                     , 5 ))
  (ListComp("Dés de vie à 1d10"                          , 10))
  (ListComp("Identification"                             , 5 ))
  (ListComp("Immunité aux charmes (niv 7)"               , 5 ))
  (ListComp("Immunité aux charmes"                       , 10))
  (ListComp("Immunité aux maladies"                      , 10))
  (ListComp("Langue des initiés"                         , 5 ))
  (ListComp("Maîtrise élémentiare"                       , 5 ))
  (ListComp("Changement de forme (dès niv 5)"            , 15))
  (ListComp("Changement de forme (niv 7)"                , 10))
  (ListComp("Passage sans traces"                        , 5 ))
  (ListComp("Purification de l'eau"                      , 5 ))
  (ListComp("Résistance au feu et à l'électricité"       , 5 ))
  (ListComp("Résistance au froid"                        , 5 ))
  (ListComp("Se cacher dans l'ombre"                     , 5 ))
  (ListComp("Spécialisation"                             , 15))
  (ListComp("Sélection de sphères standard"              , 60))
  // -> 19
  (ListComp("G\\'en\\'erale (min)"                       , 3 ))
  (ListComp("Animale (min)"                              , 5 ))
  (ListComp("Astrale (min)"                              , 3 ))
  (ListComp("Chaos (min)"                                , 5 ))
  (ListComp("Charme (min)"                               , 5 ))
  (ListComp("Climat (min)"                               , 3 ))
  (ListComp("Combat (min)"                               , 5 ))
  (ListComp("Cr\\'eation (min)"                          , 5 ))
  (ListComp("Divination (min)"                           , 5 ))
  (ListComp("El\\'ementaire (min)"                       , 8 ))
  (ListComp("Air (min)"                                  , 2 ))
  (ListComp("Eau (min)"                                  , 2 ))
  (ListComp("Feu (min)"                                  , 3 ))
  (ListComp("Terre (min)"                                , 3 ))
  (ListComp("Garde (min)"                                , 3 ))
  (ListComp("Guerre (min)"                               , 3 ))
  (ListComp("Invocation (min)"                           , 5 ))
  (ListComp("Loi (min)"                                  , 5 ))
  (ListComp("N\\'ecromancie (min)"                       , 5 ))
  (ListComp("Nombres (min)"                              , 5 ))
  (ListComp("Pens\\'ee (min)"                            , 5 ))
  (ListComp("Protection (min)"                           , 5 ))
  (ListComp("Soin (min)"                                 , 5 ))
  (ListComp("Soleil (min)"                               , 3 ))
  (ListComp("Temps (min)"                                , 5 ))
  (ListComp("V\\'eg\\'etale (min)"                       , 5 ))
  (ListComp("Vigilance (min)"                            , 5 ))
  (ListComp("Voyageur (min)"                             , 3 ))
  // -> 47
  (ListComp("G\\'en\\'erale (maj)"                       , 5 ))
  (ListComp("Animale (maj)"                              , 10))
  (ListComp("Astrale (maj)"                              , 6 ))
  (ListComp("Chaos (maj)"                                , 8 ))
  (ListComp("Charme (maj)"                               , 10))
  (ListComp("Climat (maj)"                               , 10))
  (ListComp("Combat (maj)"                               , 10))
  (ListComp("Cr\\'eation (maj)"                          , 10))
  (ListComp("Divination (maj)"                           , 10))
  (ListComp("El\\'ementaire (maj)"                       , 20))
  (ListComp("Air (maj)"                                  , 5 ))
  (ListComp("Eau (maj)"                                  , 5 ))
  (ListComp("Feu (maj)"                                  , 8 ))
  (ListComp("Terre (maj)"                                , 8 ))
  (ListComp("Garde (maj)"                                , 5 ))
  (ListComp("Guerre (maj)"                               , 5 ))
  (ListComp("Invocation (maj)"                           , 10))
  (ListComp("Loi (maj)"                                  , 8 ))
  (ListComp("N\\'ecromancie (maj)"                       , 10))
  (ListComp("Nombres (maj)"                              , 10))
  (ListComp("Pens\\'ee (maj)"                            , 10))
  (ListComp("Protection (maj)"                           , 10))
  (ListComp("Soin (maj)"                                 , 10))
  (ListComp("Soleil (maj)"                               , 5 ))
  (ListComp("Temps (maj)"                                , 10))
  (ListComp("V\\'eg\\'etale (maj)"                       , 10))
  (ListComp("Vigilance (maj)"                            , 10))
  (ListComp("Voyageur (maj)"                             , 5));
// -> 75


/** Constructeur usuel.
 * Permet la construction d'un clerc de A à Z, càd on détermine ici
 * les restrictions, puis les compétences.
 * @param xp le nombre de pts d'expérience du Druide.
 * @param trait les traits du Druide, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param PP nombre de points de personnage (PP) à disposition pour la
 * céation du Druide.
 */
Druide::Druide(unsigned int xp, unsigned int trait[7], unsigned int& PP)
  : Classe(xp, trait, Pretres)
{
  Type_classe = "Druide";
  ptrComp = &Comp;
  ptrNbrComp = &nbr_comp;
  ptrNbrRes  = &nbr_restric;
  PP += 100;
  blank() << "********************************************************" << endreq;
  blank() << "Création d'un druide" << endl << endreq;
  unsigned int nbr(0);

  blank() << "Tu as " << PP << " pp à dépenser" << endreq;

  for (unsigned int i(0); i<nbr_comp; i++)
    {
      if (Comp[i].Cout != 0)
	blank() << i+1 << ' ' << affiche_accent(Comp[i].Comp)
		    << " (" << Comp[i].Cout << " pp)" << endreq;
      else
	blank() << i+1 << ' ' << affiche_accent(Comp[i].Comp)
		    << " (cout variable)" << endreq;	
    }
  blank() << nbr_comp+1 << " Aucune" << endreq;
  blank() << "Choisir les compétences de classe" << endreq;

  bool test(true), deja_pris, encore(false);
  unsigned int save_PP(PP);
  
  do
    {
      do
	{
	  nbr = demander_unsigned_borne("("+write_int(PP)+" restants) ", 1,
					nbr_comp + 1);
      
	  if (nbr!=nbr_comp+1)
	    {
	      if (nbr != 19)
		{
		  deja_pris = safe_push_back(&Comp_classe, nbr - 1);
		  if (deja_pris)
		    warning() << "Déjà sélectionné" << endreq;
		  else
		    {
		      if (PP >= Comp[nbr-1].Cout)
			{
			  Comp_classe.push_back(nbr-1);
			  PP -= Comp[nbr-1].Cout;
			  blank() << "Compétence choisie : "
				      << affiche_accent(Comp[nbr-1].Comp) << endreq;
			}
		      else
			blank() << "Pas assez de pp" << endreq;
		    }
		}
	      else
		if (PP >= 60)
		  {
		    unsigned int spheres[11] = {70, 47, 48, 52, 56, 57, 58,
						59, 60, 69, 72};
		    for (unsigned int i(0); i<11; i++)
		      Comp_classe.push_back(spheres[i]);
		    PP -= Comp[nbr-1].Cout;
		    blank() << affiche_accent(Comp[nbr-1].Comp) << endreq;
		  }
		else
		  blank() << "Pas assez de pp" << endreq;
	    }
	  else
	    test = false;
	}
      while(test);

      affiche_competences();
      blank() << endl <<  "1 pour continuer, 2 pour recommencer" << endreq;
      nbr = demander_unsigned_borne("Choix ", 1, 2);
      if (nbr == 2)
	{
	  PP = save_PP;
	  Comp_classe.clear();
	  test = true;
	  encore = true;
	  blank() << endreq;
	}
      else
	encore = false;
    }
  while(encore);

  blank() << endl << setfill('*') << setw(56) << '*' << setfill(' ')
	      << endreq;
  calcul_niveau();
  calcul_niv_mag();
}

/** Constructeur simplifié.
 * Permet de construire un Druide lors de la lecture d'un ficier xml.
 * @param xp le nombre de pts d'expérience du Druide.
 * @param trait les traits du Druide, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 */
Druide::Druide(unsigned int xp, unsigned int trait[7])
  : Classe(xp, trait, Pretres)
{
  Type_classe = "Druide";
  ptrComp = &Comp;
  ptrNbrComp = &nbr_comp;
  ptrNbrRes  = &nbr_restric;
  calcul_niveau();
}

Druide::Druide(unsigned int xp, unsigned int trait[7], const bool& UI)
  : Classe(xp, trait, Pretres)
{
  ptrComp = &Comp;
  ptrNbrComp = &nbr_comp;
  ptrNbrRes  = &nbr_restric;
  if (!UI)
    {
      error() << "Création du personnage impossible" << endreq;
      exit(1);
    }

  Type_classe = "Druide";
  calcul_niveau();
}

Druide::~Druide()
{}

void Druide::calcul_niveau()
{
  if (XP < 7500)
    Niveau = XP / 2000 + 1;
  else if (XP < 12500)
    Niveau = 4;
  else if (XP < 20000)
    Niveau = 5;
  else if (XP < 35000)
    Niveau = 6;
  else if (XP < 125000)
    Niveau = (XP - 60000) / 30000 + 8;
  else if (XP < 200000)
    Niveau = 11;
  else if (XP < 300000)
    Niveau = 12;
  else if (XP < 3500000)
    {
      unsigned int i;
	for (i = 0; i < 4; i++)
	  {
	    if (XP < (unsigned int) 750000 * puissance(2, i))
	      break;
	  }
      Niveau = i + 12;
    }
}

// /**
//  * Il faut faire attention aux accents...
//  * @see affiche_accent
//  */
// void Druide::afficher_stats(ostream& os)
// {
//   os << "Druide" << endreq;
//   for (unsigned int i(0); i<6; i++)
//     os << Caracteristiques[i] << ' ' << Traits[i] << endreq;
//   for (unsigned int i(0); i<Comp_classe.size(); i++)
//     os << affiche_accent(Comp[Comp_classe[i]].Comp) << endreq;
// }

void Druide::appliquer_modif(const string& type)
{}

void Druide::augmenter_talents(const unsigned int& bonus, const bool& lvl_up)
{}

void Druide::get_JS(unsigned int *tab) const
{
  *(tab + 0) = 13; // Paralysie, Poison ou Mort magique
  *(tab + 1) = 14; // Bâton, batonnet baguette
  *(tab + 2) = 12; // Pétrification ou Métamorphose
  *(tab + 3) = 16; // Souffles
  *(tab + 4) = 15; // Sorts
  
  unsigned int cat;
  if (Niveau <= 4)
    cat = 0;
  else  if (Niveau <= 8)
    cat = 1;
  else  if (Niveau <= 12)
    cat = 2;
  else  if (Niveau <= 16)
    cat = 3;
  else  if (Niveau <= 20)
    cat = 4;
  else
    cat = 5;

  *(tab + 0) -= cat;
  *(tab + 1) -= cat;
  *(tab + 2) -= cat;
  *(tab + 3) -= cat;
  *(tab + 4) -= cat;
}

/**
 * Le Druide n'a pas de restrictions.
 */
string Druide::get_competence(const unsigned int& i) const
{
  if (get_comp(i) < nbr_comp)
    return affiche_accent(Comp[get_comp(i)].Comp);
  else
    return "Petit problème";
}

unsigned int Druide::calcule_TAc0() const
{
  unsigned int mod_niveau(2 * ((Niveau - 1) / 3));

  return 20 - mod_niveau;
}

void Druide::calcul_niv_mag()
{
  if (Niveau < 16)
    Niveau_mag = (Niveau + 1) / 2;
  else
    Niveau_mag = 7;
}

/**
 * Les sorts disponibles pour un Druide sont donnés par son niveau et
 * les sphères auquelles il a accès.
 *
 * La variable Classe::fonction permet d'afficher :
 *
 * FUNC  : le nom du fichier qui va être écrit
 *
 * LINFO : la vérification que la compétence est bien une sphère, ou une
 * école de magie
 *
 * LFUNC : gestion des sphères élémentaires et des accents
 *
 * DEBUG : ouverture du fichier, 
 * 
 */
void Druide::ecrire_sorts(const string& nom_fichier, const bool& ecrase) const
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
  ofstream sortie;
  string nom_complet, tmp;
  func() << "A écrire : " << nom_fichier << endreq;

  if (ecrase)
    /*
     * Ecrase l'éventuel contenu du fichier
     */
    {
      sortie.open(nom_fichier.c_str());
      debug() << "Ouverture                [ OK ]" << endreq;
    }
  else
    /*
     * Alors on ouvre le fichier de telle sorte à écrire à la suite
     */
    {
      sortie.open(nom_fichier.c_str(), ios::app);
      debug() << "Ouverture                [ OK ]" << endreq;
    }

  unsigned int taille(get_combien());
  size_t loc;

  for (unsigned int i(0); i < taille; i++)
    {
      if (get_comp(i) > 2)
	{
	  tmp = get_competence(i);
	  linfo() << "Check sphère : " << tmp << endreq;
	  loc = tmp.find(" (m", 0);
	  if (loc == string::npos)
	    {
	      lfunc() << "Pas une sphère..." << endreq;
	      continue;
	    }
	  
	  tmp.replace(loc, 6, "");
	  for (unsigned int j(1); j <= Niveau_mag; j++)
	    {
	      if (get_comp(i) < 71 && j > 3)
		/*
		 * Accès mineur => jusqu'au lvl 3 uniquement!
		 */
		break;
	      debug() << "accents loin " << supprime_accents(tmp) << endreq;
	      sortie << "\\input{../../Sorts_clerc/" << supprime_accents(tmp)
		     << "_"  << write_int(j) << ".inc.tex}" << endl;
	      
	    }
	  
	}
    }
  sortie.close();
  dout.setLevel(etat_precedent);
}

void Druide::nbr_sort(unsigned int sorts[], const unsigned int& taille) const
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
    unsigned int i;
  unsigned int tmp[7];
  func() << "Taille = " << taille << endreq;

  for (i = 0; i < taille; i++)
    tmp[i] = 0;

  switch(Niveau)
    {
    case 1:
      tmp[0] = 1;
      break;
    case 2:
      tmp[0] = 2;
      break;
    case 3:
      tmp[0] = 2;
      tmp[1] = 1;
      break;
    case 4:
      tmp[0] = 3;
      tmp[1] = 2;
      break;
    case 5:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 1;
      break;
    case 6:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 2;
      break;
    case 7:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 2;
      tmp[3] = 1;
      break;
    case 8:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 3;
      tmp[3] = 2;
      break;
    case 9:
      tmp[0] = 4;
      tmp[1] = 4;
      tmp[2] = 3;
      tmp[3] = 2;
      tmp[4] = 1;
      break;
    case 10:
      tmp[0] = 4;
      tmp[1] = 4;
      tmp[2] = 3;
      tmp[3] = 3;
      tmp[4] = 2;
      break;
    case 11:
      tmp[0] = 5;
      tmp[1] = 4;
      tmp[2] = 4;
      tmp[3] = 3;
      tmp[4] = 2;
      if (Traits[4] >= 17)
      tmp[5] = 1;
      break;
    case 12:
      tmp[0] = 6;
      tmp[1] = 5;
      tmp[2] = 5;
      tmp[3] = 3;
      tmp[4] = 2;
      if (Traits[4] >= 17)
      tmp[5] = 2;
      break;
    case 13:
      tmp[0] = 6;
      tmp[1] = 6;
      tmp[2] = 6;
      tmp[3] = 4;
      tmp[4] = 2;
      if (Traits[4] >= 17)
      tmp[5] = 2;
      break;
    case 14:
      tmp[0] = 6;
      tmp[1] = 6;
      tmp[2] = 6;
      tmp[3] = 5;
      tmp[4] = 3;
      if (Traits[4] >= 17)
      tmp[5] = 2;
      if (Traits[4] >= 18)
      tmp[6] = 1;
      break;
    case 15:
      tmp[0] = 6;
      tmp[1] = 6;
      tmp[2] = 6;
      tmp[3] = 6;
      tmp[4] = 4;
      if (Traits[4] >= 17)
	tmp[5] = 2;
      if (Traits[4] >= 18)
	tmp[6] = 1;
      break;
    case 16:
      tmp[0] = 7;
      tmp[1] = 7;
      tmp[2] = 7;
      tmp[3] = 6;
      tmp[4] = 4;
      if (Traits[4] >= 17)
	tmp[5] = 3;
      if (Traits[4] >= 18)
	tmp[6] = 1;
      break;
    case 17:
      tmp[0] = 7;
      tmp[1] = 7;
      tmp[2] = 7;
      tmp[3] = 7;
      tmp[4] = 5;
      if (Traits[4] >= 17)
	tmp[5] = 3;
      if (Traits[4] >= 18)
	tmp[6] = 2;
      break;
    case 18:
      tmp[0] = 8;
      tmp[1] = 8;
      tmp[2] = 8;
      tmp[3] = 8;
      tmp[4] = 6;
      if (Traits[4] >= 17)
	tmp[5] = 4;
      if (Traits[4] >= 18)
	tmp[6] = 2;
      break;
    case 19:
      tmp[0] = 9;
      tmp[1] = 9;
      tmp[2] = 8;
      tmp[3] = 8;
      tmp[4] = 6;
      if (Traits[4] >= 17)
	tmp[5] = 4;
      if (Traits[4] >= 18)
	tmp[6] = 2;
      break;
    default:
      tmp[0] = 9;
      tmp[1] = 9;
      tmp[2] = 9;
      tmp[3] = 8;
      tmp[4] = 7;
      if (Traits[4] >= 17)
	tmp[5] = 5;
      if (Traits[4] >= 18)
	tmp[6] = 2;
      break;
    }

  for (i = 0; i < Niveau_mag; i++)
    sorts[i] = tmp[i];

  unsigned int compteur1(0), compteur2(0), compteur3(0), compteur4(0);

  if (Traits[4] >= 13 && Traits[4] < 18)
    {
      compteur1 = (Traits[4] - 11) / 2;
      compteur2 = (Traits[4] - 12) / 2;
    }
  else if (Traits[4] >= 18 && Traits[4] < 19)
    {
      compteur1 = Traits[4] - 14;
      compteur2 = Traits[4] - 16;
    }
  else if (Traits[4] >= 19 && Traits[4] < 23)
    {
      compteur1 = (Traits[4] + 1) / 2 - 6;
      compteur2 = Traits[4] / 2 - 6;
      compteur3 = Traits[4] - 18;
    }
  else
    {
      compteur1 = (Traits[4] + 1) / 2 - 6;
      compteur2 = Traits[4] / 2 - 6;
      compteur3 = Traits[4] - 19;
      compteur4 = 1;
    }

  for (i = 0; i < compteur1; i++)
    {
      if (i == Niveau_mag)
	break;
      sorts[i]++;
    }
  for (i = 0; i < compteur2; i++)
    {
      if (i == Niveau_mag)
	break;
      sorts[i]++;
    }
  for (i = 0; i < compteur3; i++)
    {
      if (i == Niveau_mag)
	break;
      sorts[i]++;
    }
  for (i = 0; i < compteur4; i++)
    {
      if (i == Niveau_mag)
	break;
      sorts[i]++;
    }

  dout.setLevel(etat_precedent);
}

/**
 * Permet de gérer les compétences martiales, càd lesquelles sont accessibles
 * à quel coût.
 *
 * Un Druide n'a qu'une sélection limitée d'armes, dont le coût est de 3 pp.
 */
void Druide::get_prix_acces(map<string, unsigned int>* Map) const
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
  func() << "Duide::get_prix_acces -> début" << endreq;

  map<string, unsigned int>::const_iterator mapIt, mapBegin(Map -> begin()),
    mapEnd(Map -> end());

  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
    {
      lfunc() << mapIt -> first << endreq;
    }
  Map -> operator[]("Druide") = 3;
  
  func() << "Druide::ajout_sorts -> fin" << endreq;
  dout.setLevel(etat_precedent);
}
